!------------------------------------------------------------------------------
! TJU/Department of Mechanics, Fluid Mechanics, Code START
!------------------------------------------------------------------------------
!> @Author: Jianxin Liu shookware@tju.edu.cn
!> @Date: 2024-08-13 21:19:07
!> @LastEditTime: 2024-08-13 21:19:08
!> @LastEditors: Jianxin Liu shookware@tju.edu.cn
!> @Description:
!> @FilePath: /START_NS_3D/src/splitter/module_splitter_base.f90
!> @Copyright (c) 2024 by Jianxin Liu email: shookware@tju.edu.cn, All Rights Reserved.
!------------------------------------------------------------------------------
module mod_splitter

   use iso_fortran_env, only: dp => real64
   use petsc_wrapper
   use mod_eos_base
   implicit none

   private

   public :: type_splitter_base, create_splitter, destroy_splitter

#include <start.h90>
   type, abstract :: type_splitter_base
      integer :: splitter_type = -1
      class(type_eos), pointer :: eos => null()
   contains

      procedure(split_interface), deferred :: split
      procedure(split2_interface), deferred :: split2

      ! generic :: create => CreateEOSSplitter, CreateVectorEOSSplitter
   end type type_splitter_base

   interface create_splitter
      module procedure :: CreateEOSSplitter
   end interface

   interface destroy_splitter
      module procedure :: DestroySplitter
   end interface

   abstract interface
      subroutine split_interface(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         import
         class(type_splitter_base), intent(inout) :: this
         class(type_vector_base), intent(in) :: q_in(:)
         class(type_vector_base), intent(inout) :: q_l(:), q_r(:)
         class(type_vector_base), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine split_interface

      subroutine split2_interface(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         import
         class(type_splitter_base), intent(inout) :: this
         type(type_self_vector), intent(in) :: q_in(:)
         type(type_self_vector), intent(inout) :: q_l(:), q_r(:)
         type(type_self_vector), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine split2_interface
   end interface

   type, extends(type_splitter_base) :: type_splitter_steger_warming

   contains
      procedure :: split => SplitStegerWarming
      procedure :: split2 => SplitStegerWarming2

   end type type_splitter_steger_warming

   interface
      module subroutine SplitStegerWarming(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_steger_warming), intent(inout) :: this
         class(type_vector_base), intent(in) :: q_in(:)
         class(type_vector_base), intent(inout) :: q_l(:), q_r(:)
         class(type_vector_base), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitStegerWarming
      module subroutine SplitStegerWarming2(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_steger_warming), intent(inout) :: this
         type(type_self_vector), intent(in) :: q_in(:)
         type(type_self_vector), intent(inout) :: q_l(:), q_r(:)
         type(type_self_vector), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitStegerWarming2
   end interface

   type, extends(type_splitter_base) :: type_splitter_van_leer

   contains
      procedure :: split => SplitVanLeer
      procedure :: split2 => SplitVanLeer2

   end type type_splitter_van_leer

   interface
      module subroutine SplitVanLeer(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_van_leer), intent(inout) :: this
         class(type_vector_base), intent(in) :: q_in(:)
         class(type_vector_base), intent(inout) :: q_l(:), q_r(:)
         class(type_vector_base), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitVanLeer
      module subroutine SplitVanLeer2(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_van_leer), intent(inout) :: this
         type(type_self_vector), intent(in) :: q_in(:)
         type(type_self_vector), intent(inout) :: q_l(:), q_r(:)
         type(type_self_vector), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitVanLeer2
   end interface

   type, extends(type_splitter_base) :: type_splitter_local_lax_friedrichs

   contains
      procedure :: split => SplitLocalLaxFriedrichs
      procedure :: split2 => SplitLocalLaxFriedrichs2

   end type type_splitter_local_lax_friedrichs

   interface
      module subroutine SplitLocalLaxFriedrichs(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_local_lax_friedrichs), intent(inout) :: this
         class(type_vector_base), intent(in) :: q_in(:)
         class(type_vector_base), intent(inout) :: q_l(:), q_r(:)
         class(type_vector_base), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitLocalLaxFriedrichs
      module subroutine SplitLocalLaxFriedrichs2(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_local_lax_friedrichs), intent(inout) :: this
         type(type_self_vector), intent(in) :: q_in(:)
         type(type_self_vector), intent(inout) :: q_l(:), q_r(:)
         type(type_self_vector), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitLocalLaxFriedrichs2
   end interface

   type, extends(type_splitter_base), abstract :: type_splitter_ausm_base

   contains
      procedure :: split => SplitAUSM
      procedure :: split2 => SplitAUSM2

   end type type_splitter_ausm_base

   type, extends(type_splitter_ausm_base) :: type_splitter_ausm

   end type type_splitter_ausm

   type, extends(type_splitter_ausm_base) :: type_splitter_ausm_plus

   end type type_splitter_ausm_plus

   interface
      module subroutine SplitAUSM(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_ausm_base), intent(inout) :: this
         class(type_vector_base), intent(in) :: q_in(:)
         class(type_vector_base), intent(inout) :: q_l(:), q_r(:)
         class(type_vector_base), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitAUSM
      module subroutine SplitAUSM2(this, q_in, q_l, q_r, chi_x, chi_y, chi_z, chi_face)
         class(type_splitter_ausm_base), intent(inout) :: this
         type(type_self_vector), intent(in) :: q_in(:)
         type(type_self_vector), intent(inout) :: q_l(:), q_r(:)
         type(type_self_vector), intent(in) :: chi_x, chi_y, chi_z, chi_face
      end subroutine SplitAUSM2
   end interface

   type :: type_array
      real(dp), pointer :: p(:)
   end type type_array

contains

   subroutine CreateEOSSplitter(this, splitter_type, eos)
      implicit none
      class(type_splitter_base), intent(inout), allocatable :: this
      integer, intent(in) :: splitter_type
      class(type_eos), target, intent(in) :: eos

      select case (splitter_type)
      case (FLUX_SPLIT_STEGER_WARMING)
         call petsc_print("steger-warming splitter is selected.")
         allocate (type_splitter_steger_warming :: this)
      case (FLUX_SPLIT_LOCAL_LAX_FRIEDRICHS)
         call petsc_print("local lax-friedrichs splitter is selected.")
         allocate (type_splitter_local_lax_friedrichs :: this)
      case (FLUX_SPLIT_AUSM)
         call petsc_print("ausm splitter is selected.")
         allocate (type_splitter_ausm :: this)
      case (FLUX_SPLIT_AUSM_PLUS)
         call petsc_print("ausm splitter is selected.")
         allocate (type_splitter_ausm_plus :: this)
      case (FLUX_SPLIT_VAN_LEER)
         call petsc_print("van-leer splitter is selected.")
         allocate (type_splitter_van_leer :: this)
      case default
         call petsc_error_print("START_NS_3D:[ERROR]scheme_splitter is not defined.")
      end select

      this%eos => eos
      this%splitter_type = splitter_type

   end subroutine CreateEOSSplitter

   subroutine DestroySplitter(this)
      implicit none
      class(type_splitter_base), allocatable, intent(inout) :: this

      this%eos => null()
      this%splitter_type = -1
      deallocate (this)

   end subroutine DestroySplitter

end module mod_splitter
